﻿using System;
using System.IO;
using System.Net;

namespace SomeKit.Http
{
    public static partial class HttpHelper
    {
        /// <summary>
        /// Helper class to contain std state when invoking async calls with HttpWebRequest.
        /// </summary>
        private class AsyncState
        {
            /// <summary>
            /// The HttpWebRequest, that was used to perform the actual call.
            /// </summary>
            public HttpWebRequest Request { get; set; }
            /// <summary>
            /// The handler for the response of the server.
            /// </summary>
            public Action<string> FinalHandler { get; set; }
            /// <summary>
            /// The error handler.
            /// </summary>
            public Action<Exception> ErrorHandler { get; set; }
        }

        /// <summary>
        /// Specialization for calls, that provide a request body.
        /// </summary>
        private class AsyncStateWithBody : AsyncState
        {
            /// <summary>
            /// The string body of the request being made.
            /// </summary>
            public string Body { get; set; }
        }

        /// <summary>
        /// The async intermediate handler that fills in the body of the request.
        /// </summary>
        /// <param name="asyncResult">An instance of a <see cref="AsyncStateWithBody"/>, whose body (if present) is appended to the request stream.</param>
        private static void GetRequestStreamCallback(IAsyncResult asyncResult)
        {
            var state = asyncResult.AsyncState as AsyncStateWithBody;
            try
            {
                var stream = state.Request.EndGetRequestStream(asyncResult);
                if (!string.IsNullOrEmpty(state.Body))
                {
                    var bytes = System.Text.Encoding.UTF8.GetBytes(state.Body);
                    stream.Write(bytes, 0, bytes.Length);
                    stream.Close();
                }
                state.Request.BeginGetResponse(new AsyncCallback(GetResponseCallback), state);
            }
            catch (Exception e)
            {
                if (state.ErrorHandler != null)
                    state.ErrorHandler(e);
            }
        }

        /// <summary>
        /// The final handler for all requests - with and without a body. The response is read from the response stream and sent to the final handler provided with the original call.
        /// </summary>
        /// <param name="asyncResult">An instance of an <see cref="AsyncState"/> (or <see cref="AsyncStateWithBody"/ if a body is part of the request)></param>
        private static void GetResponseCallback(IAsyncResult asyncResult)
        {
            var state = asyncResult.AsyncState as AsyncState;
            try
            {
                var response = state.Request.EndGetResponse(asyncResult);
                var responseString = response.WebResponseAsString();
                if (state.FinalHandler != null)
                    state.FinalHandler(responseString);
            }
            catch (Exception e)
            {
                if (state.ErrorHandler != null)
                    state.ErrorHandler(e);
            }
        }
    }
}
